package com.jcp.util;

import java.lang.reflect.InvocationTargetException;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;

/**
 * jdbc访问通用类
 * 
 * @author Administrator
 *
 */
public class DBUtil {


    

	private final static String URL = "jdbc:mysql://localhost:3307/jcp?useUnicode=true&characterEncoding=UTF8";
	private final static String USER = "root";
	private final static String PASSWORD = "root";
	private final static String DRIVER = "com.mysql.jdbc.Driver";

	/**
	 * 获取连接对象
	 * 
	 * @return 是一个java.sql.Connection的对象
	 */
	public static Connection getConn() {
		Connection conn = null;
		try {
			// 加载驱动
			Class.forName(DRIVER);
			// 得到连接对象
			conn = DriverManager.getConnection(URL, USER, PASSWORD);
		} catch (ClassNotFoundException | SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// 返回连接对象
		return conn;
	}

	/**
	 * 创建并配置preparedstatement
	 * 
	 * @param params
	 *            占位符的位置的值
	 *
	 */
	public static void setPreparedStatement(PreparedStatement pstmt, Object... params) {
		try {
			// 可以将params看成是一个数组，就可以遍历数据
			if (params != null) {
				for (int i = 0; i < params.length; i++) {
					pstmt.setObject(i + 1, params[i]);
				}
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	/**
	 * 执行增加删除 修改的操作
	 * 
	 * @param sql
	 *            执行的sql语句
	 * @param params
	 *            占位符的参数值
	 * @return 返回值 为受影响的行
	 */
	public static int exUpdate(String sql, Object... params) {
		Connection conn = null;
		PreparedStatement pstmt = null;
		int result = 0;
		// 增加删除修改的操作 实际上要调用的是preparedStatement对象的executeUpdate方法
		try {
			conn = getConn();
			pstmt = conn.prepareStatement(sql);
			// 需要将占位符的参数补齐
			setPreparedStatement(pstmt, params);
			result = pstmt.executeUpdate();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} // 释放资源
		finally {
			closeAll(null, pstmt, conn);
		}
		return result;
	}

	// 查询的操作[员工 部门....]
	public static List exQuery(String sql, Class cla, Object... params) {
		List list = new ArrayList<>();
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		// 增加删除修改的操作 实际上要调用的是preparedStatement对象的executeUpdate方法
		conn = getConn();
		try {
			pstmt = conn.prepareStatement(sql);
			// 需要将占位符的参数补齐
			setPreparedStatement(pstmt, params);
			// 执行查询操作
			rs = pstmt.executeQuery();

			// 这里对 cla 判断一下
			if (cla.newInstance().getClass().getName().equals("java.lang.Object")) {
				if (rs.next()) {
					//System.out.println("rs.getObject(1) :" + rs.getObject(1));
					list.add(rs.getObject(1)); // 集合只有一个元素，int类型值
				}
			} else {
				while (rs.next()) // 行记录 ,几行，rs就循环几次
				{
					Object obj = convert(rs, cla);
					// 完整的对象
					// System.out.println("obj :" + obj);
					list.add(obj);

				}
			}
		} catch (SQLException | InstantiationException | IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			closeAll(rs, pstmt, conn);
		}
		// 解决的关键为题是:
		// 查询结果 如何和我们的实体类对象 结合在一起?
		// 如果这里查询的结果是 deptNo deptName -》如何将deptNo赋值给Dept类的对象dept.setDeptNo
		// deptName -》如何将deptName赋值给Dept类的对象dept.setDeptName
		return list;
	}

	/**
	 * 将ResultSet得到的结果和cla对象进行转换
	 * 
	 * @param rs
	 *            ResultSet结果集
	 * @param cla
	 *            要转换的对象的类型[Class类型]
	 * @return Object 实际的类型的对象[转转结果]
	 */
	private static Object convert(ResultSet rs, Class cla) {
		// 先得到这个Dept对象，怎么得到? 同时又不确定是哪个类对象，此时需要借助Class类型.
		Object obj = null;

		// 这个问题是Beanutil本身就是这样设计的.
		// 如果用到了date请自己完成注册器
		ConvertUtils.register(new Converter() {

			SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:SS");

			@Override
			public Object convert(Class arg0, Object v) {
				// TODO Auto-generated method stub
				// 结局的问题是 如何将数据库结果[字符串] ->对象[java.util.Date]
				// 方法参数v是要转换的值
				try {
					return simpleDateFormat.parse(v.toString());
				} catch (ParseException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				return null;
			}
		}, Date.class); // java.util.Date

		// 注册器 注册结束，我们的beanutil可以解析Date类型了

		try {
			obj = cla.newInstance();
			ResultSetMetaData rsm = rs.getMetaData();
			// 查询语句中的 对应的列数量
			int columnCount = rsm.getColumnCount();
			// 需要使用循环来进行赋值
			for (int i = 0; i < columnCount; i++) {
				// 得到[查询语句中的]列名[备注:如果起了别名，那么将是"别名"] [列名都是1开始]
				String cname = rsm.getColumnLabel(i + 1);

				// System.out.println("cname :" + cname);
				Object value = rs.getObject(cname);
				// System.out.println("value :" + value);
				// 经过这个代码之后，obj属性应该已经有值了
				if (value != null)
					BeanUtils.setProperty(obj, cname, value); // 给obj对象的属性deptNo赋值为1
			}

		} catch (InstantiationException | IllegalAccessException | SQLException | InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return obj;

	}

	/**
	 * 分页的通用方法，mysql数据库
	 * 
	 * @param sql
	 * @param cla
	 * @param page
	 *            页码
	 * @param pageSize
	 *            每页显示的记录数
	 * @param params
	 * @return
	 */
	public static PageData exQueryByPage(String sql, Class cla, int page, int pageSize, Object... params) {
		// 问题:这里来加limit 还是传递过来之间就加好limit？
		// select .... from ... where ? ... limit ?,?

		String newsql = "select count(1) from (" + sql + ")  as t";
		// 如果传递的是object对象，查询操作得到就是单个结果
		List list1 = exQuery(newsql, Object.class, params);

		int totalCount = Integer.parseInt(list1.get(0) + "");
		// 可以加page的判断
		if (page < 1) {
			page = 1;
		}

		int start = (page - 1) * pageSize;
		sql = sql + " limit " + start + "," + pageSize;

		// 页面展示的数据集
		List data = exQuery(sql, cla, params);

		PageData pageData = new PageData<>(data, page, totalCount, pageSize);

		return pageData;
	}

	/**
	 * 释放资源
	 * 
	 * @param rs
	 *            结果集对象
	 * @param pstmt
	 *            预处理对象
	 * @param conn
	 *            连接对象
	 */
	public static void closeAll(ResultSet rs, PreparedStatement pstmt, Connection conn) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if (pstmt != null) {
			try {
				pstmt.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

}
